/**
 * Copyright (c) 2010-2011 julewa.com.  
 * All rights reserved. 
 * 
 * @author Huang Benhua
 * @date 2011-2-20
 * 
**/
package com.julewa.db.info;

/**
 * TODO 暂且没有支持子结构
 */
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;

import com.julewa.SystemContext;
import com.julewa.TODO;
import com.julewa.db.BeanInfo;
import com.julewa.db.DB;
import com.julewa.db.DataState;
import com.julewa.db.DataType;
import com.julewa.db.PropertyType;
import com.julewa.db.SearchDefine;
import com.julewa.db.StringTrimmer;
import com.julewa.db.checkers.DataStateChecker;
import com.julewa.loading.SystemUtils;

public class PropertyInfo implements BeanInfo.ColumnInfo{
	
	private Class<?> orignalType;
	private DataType dataType = null;
	private PropertyType propertyType = PropertyType.OBJECT;
	private boolean key = false;
	private String code = null;
	private String label = null; 
//	private String typename = null;  
	private SearchDefine searchDefine = null;
	private StringTrimmer autotrim = null;
	private double min = 0; 
	private double max = 0;
	private boolean nullable=false; 
	//
	public PropertyInfo(Method method, Class<?> real){
		//
		DB.COLUMN c = method.getAnnotation(DB.COLUMN.class);
		DB.KEY k = method.getAnnotation(DB.KEY.class);
		this.searchDefine = new SearchDefine(method.getAnnotation(DB.SEARCH.class));
		//
		if(k == null && c == null)return;
		key = (k != null);
		code = SystemUtils.getPropertyName(method);
		label = code;
		if(k == null)loadInfo(c);else loadInfo(k); 
		try {
			//if(code.equals("ID") && Entity.class.isAssignableFrom(real)){
			//	this.field = method.getDeclaringClass().getDeclaredField("_ID_");
			//}else{
				this.field = method.getDeclaringClass().getDeclaredField(code);
			//}
			this.field.setAccessible(true);
		} catch (Exception e) {
			System.out.println("Fatal error, field [" + real + ":" + code + "] not found!");
			code = null;
			e.printStackTrace();
		}
		if(code == null)return;
		// 
		this.orignalType = method.getReturnType();
		this.dataType = DataType.of(orignalType);
		
		//typename = TypenameOf(orignalType);
		if(dataType == null && code.equals("ID")){
			dataType = DataType.of(SystemContext.getEntityKeyType(real));
		} 
		propertyType =PropertyType.of(orignalType);
		//
		if(dataType.equals(DataType.STRING)){
			this.autotrim   = new StringTrimmer(method.getAnnotation(DB.AUTOTRIM.class));
			if(this.max == Double.MAX_VALUE){
				this.max = DB.TEXT_LENGTH_DEFAULT;
			}
		}
	}
	
	public PropertyInfo(Field field){
		if(Modifier.isStatic(field.getModifiers()))return;
		DB.COLUMN c = field.getAnnotation(DB.COLUMN.class);
		DB.KEY k = field.getAnnotation(DB.KEY.class);
		this.searchDefine = new SearchDefine(field.getAnnotation(DB.SEARCH.class));
		if(k == null && c == null)return;
		key = (k != null);
		this.field = field;
		this.field.setAccessible(true);
		if(k == null)loadInfo(c);else loadInfo(k); 
		code = field.getName();
		label = code;
		orignalType = field.getType();
		this.dataType = DataType.of(orignalType); 
		propertyType =PropertyType.of(orignalType);
		//dbname = code.toUpperCase();
		if(dataType.equals(DataType.STRING)){
			this.autotrim   = new StringTrimmer(field.getAnnotation(DB.AUTOTRIM.class));
			if(this.max == Double.MAX_VALUE){
				this.max = DB.TEXT_LENGTH_DEFAULT;
			}
		}
	}

	//TODO 如果要国际化的话,得修改这里
	public String getLabel(){
		return label;
	}
	public void setLabel(String l){
		label = l;
	}
		
	boolean isValid(){
		return dataType != null;
	}
	
	public boolean isKey() {
		return key;
	}
 
	public String getCode() {
		return code;
	}
	public Class<?> getTypeClass(){
		return orignalType;
	}
	
	public SearchDefine getSearchDefine(){
		return this.searchDefine;
	}
	// 
	public double getMin(){
		return min;
	}
	public double getMax(){
		return max;
	}
	public boolean isCheckMin(){
		return min != Double.MIN_VALUE;
	}
	public boolean isCheckMax(){
		return max != Double.MAX_VALUE;
	}
	public StringTrimmer getAutoTrimmer(){
		return autotrim;
	}
	public boolean isNullable(){
		return nullable;
	}
	public PropertyType getPropertyType(){
		return propertyType;
	}
	public DataType getDataType(){
		return dataType;
	}
	//所有的属性,最后都落实到field上,
	//因此检测数据只要field就够了
	private Field field = null;
	private DataStateChecker checker = null;
	public DataState checkDataState(Object obj){
		if(obj == null)return DataState.IS_NULL;
		if(checker == null){
			checker = dataType.generateChecker();
			checker.initialize(propertyType == PropertyType.PRIMARY, nullable, min, max);//, format);
		}
		//auto trim string datas
		try {
			Object val = field.get(obj);
			if(autotrim != null && val != null){
				field.set(obj, autotrim.trim((String)val));
			}
			//
			if(propertyType.isCollectionType()){
				throw new TODO("暂且还不支持集合类型耶~");
			} else {
				return checker.check(val);
			}
		} catch (Exception e) {
			e.printStackTrace();
			throw new TODO("还没做处理,也许不用做");
		}
	};
	
	//就不检查错误了
	public <T> void clone(T from, T to) throws Exception{
		Object vto = field.get(to);
		Object vfrom = field.get(from);
		if(vfrom == null || !vfrom.equals(vto)){
			this.clearRelativeFields(to);
		}
		field.set(to, vfrom);
	}
	
	//
	List<Field> relativeFields = new ArrayList<Field>();
	public void clearRelativeFields(Object bean) throws Exception{
		for(Field f: relativeFields){
			f.set(bean, null);
		}
	};
	
	//
	private void loadInfo(DB.KEY c){
		this.min = c.min();
		this.max = c.max();
		//this.format = c.format();
		this.nullable = false; 
	}
	private void loadInfo(DB.COLUMN c){
		this.min = c.min();
		this.max = c.max();
		//this.format = c.format();
		this.nullable = c.nullable(); 
	}
	

	public String toString(){
		return (key?"[key]":"") + code + ">" + dataType.getLabel(); 
	}
	
	public static final void main(String args[]){
		System.out.println(Number.class.isAssignableFrom(Integer.class));
	}

	@Override
	public BeanInfo getPropertyBeanInfo() {
		//
		return null;
	}
	

 
}